#ifndef _AS_STEERINGBEHAVIORS_H_
#define _AS_STEERINGBEHAVIORS_H_
//#pragma warning (disable:4786)

#include "ASCommon.h"
#include "Vector2D.h"
#include "ASPath.h"
#include "ASWall2D.h"
#include "ASParamLoader.h"
NS_AS_BEGIN


class ASEntity;
class ASMobileComponent;


//--------------------------- Constants ----------------------------------

////the radius of the constraining circle for the wander behavior
//const double WanderRad    = 1.2;
////distance the wander circle is projected in front of the agent
//const double WanderDist   = 2.0;
////the maximum amount of displacement along the circle each frame
//const double WanderJitterPerSec = 80.0;

//used in path following
const double WaypointSeekDist   = 1;//20;                                          



//------------------------------------------------------------------------



class ASSteeringBehavior
{
public:

	//enum summing_method{weighted_average, prioritized, dithered};
	enum BehaviorType
	{
		none                = 0x000000,
		seek                = 0x000001,
		flee                = 0x000002,
		arrive              = 0x000004,
		wander              = 0x000008,
		cohesion            = 0x000010,
		separation          = 0x000020,
		allignment          = 0x000040,
		obstacle_avoidance  = 0x000080,
		wall_avoidance      = 0x000100,
		follow_path         = 0x000200,
		pursuit             = 0x000400,
		evade               = 0x000800,
		interpose           = 0x001000,
		hide                = 0x002000,
		flock               = 0x004000,
		offset_pursuit      = 0x008000,
		forward_resistance  = 0x010000,
		approach		    = 0x020000,
		approach_horizon	= 0x040000,
		crowd_path		    = 0x080000,
	};

private:
	ASMobileComponent* m_pMobile;   
	Vector2D    m_vSteeringForce;
	ASEntity*     m_pTargetAgent1;
	ASEntity*     m_pTargetAgent2;
	double				   m_dTargetDistance;
	std::vector<Vector2D> m_Feelers;
	double                 m_dWallDetectionFeelerLength;
	ASPath*       m_pPath;
	double        m_dWaypointSeekDistSq;
	Vector2D     m_vOffset;
	int	m_iApproachRange;
	int           m_iFlags;
	
	
	enum Deceleration{slow = 3, normal = 2, fast = 1};
	Deceleration m_Deceleration;
	


	bool      AccumulateForce(Vector2D& RunningTot,const Vector2D& ForceToAdd,double MaxForce);

	//creates the antenna utilized by the wall avoidance behavior
	void      CreateFeelers();



	/* .......................................................

	BEGIN BEHAVIOR DECLARATIONS

	.......................................................*/


	//this behavior moves the agent towards a target position
	Vector2D Seek(Vector2D TargetPos);

	//this behavior returns a vector that moves the agent away
	//from a target position
	Vector2D Flee(Vector2D TargetPos);

	//this behavior is similar to seek but it attempts to arrive 
	//at the target position with a zero velocity
	Vector2D Arrive(Vector2D     TargetPos,
		Deceleration deceleration);

	//this behavior predicts where an agent will be in time T and seeks
	//towards that point to intercept it.
	Vector2D Pursuit(ASEntity* agent);

	//this behavior maintains a position, in the direction of offset
	//from the target vehicle
	Vector2D OffsetPursuit(ASEntity* agent, const Vector2D offset);

	//this behavior attempts to evade a pursuer
	Vector2D Evade(ASEntity* agent);

	//this behavior makes the agent wander about randomly
	//Vector2D Wander();

	//this returns a steering force which will attempt to keep the agent 
	//away from any obstacles it may encounter
	Vector2D ObstacleAvoidance(const std::vector<ASEntity*>& obstacles);

	//this returns a steering force which will keep the agent away from any
	//walls it may encounter
	Vector2D WallAvoidance(const std::vector<ASWall2D> &walls);


	//given a series of Vector2Ds, this method produces a force that will
	//move the agent along the waypoints in order
	Vector2D FollowPath();

	Vector2D CrowdPath(const std::vector<ASEntity*> &neighbors,float dt);

	//this results in a steering force that attempts to steer the vehicle
	//to the center of the vector connecting two moving agents.
	Vector2D Interpose(ASEntity* VehicleA, ASEntity* VehicleB);

	//given another agent position to hide from and a list of BaseGameEntitys this
	//method attempts to put an obstacle between itself and its opponent
	Vector2D Hide(ASEntity* hunter, const std::vector<ASEntity*>& obstacles);

	Vector2D ForwardResistance(const std::vector<ASEntity*> &neighbors);

	Vector2D Approach(ASEntity* pTarget,int range);

	Vector2D ApproachHorizon(ASEntity* pTarget,int range,float dt);

	// -- Group Behaviors -- //

	Vector2D Cohesion(const std::vector<ASEntity*> &neighbors);

	Vector2D Separation(const std::vector<ASEntity*> &neighbors);

	Vector2D Alignment(const std::vector<ASEntity*> &neighbors);



	//the following three are the same as above but they use cell-space
	//partitioning to find the neighbors
	//Vector2D CohesionPlus(const std::vector<ASMobileComponent*> &agents);
	//Vector2D SeparationPlus(const std::vector<ASMobileComponent*> &agents);
	//Vector2D AlignmentPlus(const std::vector<ASMobileComponent*> &agents);

	/* .......................................................

	END BEHAVIOR DECLARATIONS

	.......................................................*/

	//calculates and sums the steering forces from any active behaviors
	//Vector2D CalculateWeightedSum();
	//Vector2D CalculatePrioritized();
	//Vector2D CalculateDithered();

	//helper method for Hide. Returns a position located on the other
	//side of an obstacle to the pursuer
	Vector2D GetHidingPosition(const Vector2D& posOb,
		const double     radiusOb,
		const Vector2D& posHunter);

	//this function tests if a specific bit of m_iFlags is set
	bool      On(BehaviorType bt){return (m_iFlags & bt) == bt;}

public:

	ASSteeringBehavior(ASMobileComponent* agent);

	virtual ~ASSteeringBehavior();

	

	//calculates and sums the steering forces from any active behaviors
	//Vector2D Calculate();

	Vector2D Calculate(float dt);

	//calculates the component of the steering force that is parallel
	//with the vehicle heading
	double    ForwardComponent();

	//calculates the component of the steering force that is perpendicuar
	//with the vehicle heading
	double    SideComponent();



	//renders visual aids and info for seeing how each behavior is
	//calculated
	//void      RenderAids();

	//void      SetTarget(const Vector2D& t){m_vTarget = t;}

	void      SetTargetAgent1(ASEntity* Agent){m_pTargetAgent1 = Agent;}
	void      SetTargetAgent2(ASEntity* Agent){m_pTargetAgent2 = Agent;}

	void	  SetApproachRange(int range){m_iApproachRange = range;};
	int		  GetApproachRange(){return m_iApproachRange;};
	void      SetOffset(const Vector2D& offset){m_vOffset = offset;}
	Vector2D  GetOffset()const{return m_vOffset;}

	void      SetPath(const std::vector<Vector2D>& new_path){m_pPath->Set(new_path);}
	ASPath*   GetPath(){return m_pPath;}


	void      CreateRandomPath(int num_waypoints, int mx, int my, int cx, int cy)const
	{m_pPath->CreateRandomPath(num_waypoints, mx, my, cx, cy);}

	Vector2D Force()const{return m_vSteeringForce;}

	//void      ToggleSpacePartitioningOnOff(){m_bCellSpaceOn = !m_bCellSpaceOn;}
	//bool      isSpacePartitioningOn()const{return m_bCellSpaceOn;}

	//void      SetSummingMethod(summing_method sm){m_SummingMethod = sm;}

	bool IsRunning(){return m_iFlags!=0;}
	void FleeOn(){m_iFlags |= flee;}
	void SeekOn(){m_iFlags |= seek;}
	void ArriveOn(){m_iFlags |= arrive;}
	void WanderOn(){m_iFlags |= wander;}
	void PursuitOn(ASEntity* v){m_iFlags |= pursuit; SetTargetAgent1(v);}
	void EvadeOn(ASEntity* v){m_iFlags |= evade; SetTargetAgent1(v);}
	void CohesionOn(){m_iFlags |= cohesion;}
	void SeparationOn(){m_iFlags |= separation;}
	void AlignmentOn(){m_iFlags |= allignment;}
	void ObstacleAvoidanceOn(){m_iFlags |= obstacle_avoidance;}
	void WallAvoidanceOn(){m_iFlags |= wall_avoidance;}
	void FollowPathOn(){m_iFlags |= follow_path;}
	void InterposeOn(ASEntity* v1, ASEntity* v2){m_iFlags |= interpose; SetTargetAgent1(v1); SetTargetAgent2(v2);}
	void HideOn(ASEntity* v){m_iFlags |= hide; SetTargetAgent1(v);}
	void OffsetPursuitOn(ASEntity* v1, const Vector2D& offset){m_iFlags |= offset_pursuit; m_vOffset = offset; SetTargetAgent1(v1);}  
	void FlockingOn(){CohesionOn(); AlignmentOn(); SeparationOn(); WanderOn();}
	void ForwardResistanceOn(){m_iFlags |= forward_resistance;}
	void ApproachOn(ASEntity* e,int range){m_iFlags |= approach;SetTargetAgent1(e);SetApproachRange(range);}
	void ApproachHorizonOn(ASEntity* e,int  range){m_iFlags |= approach_horizon;SetTargetAgent1(e);SetApproachRange(range);}
	void CrowdPathOn(){m_iFlags |= crowd_path;}	

	void FleeOff()  {if(On(flee))   m_iFlags ^=flee;}
	void SeekOff()  {if(On(seek))   m_iFlags ^=seek;}
	void ArriveOff(){if(On(arrive)) m_iFlags ^=arrive;}
	void WanderOff(){if(On(wander)) m_iFlags ^=wander;}
	void PursuitOff(){if(On(pursuit)) m_iFlags ^=pursuit;}
	void EvadeOff(){if(On(evade)) m_iFlags ^=evade;}
	void CohesionOff(){if(On(cohesion)) m_iFlags ^=cohesion;}
	void SeparationOff(){if(On(separation)) m_iFlags ^=separation;}
	void AlignmentOff(){if(On(allignment)) m_iFlags ^=allignment;}
	void ObstacleAvoidanceOff(){if(On(obstacle_avoidance)) m_iFlags ^=obstacle_avoidance;}
	void WallAvoidanceOff(){if(On(wall_avoidance)) m_iFlags ^=wall_avoidance;}
	void FollowPathOff(){if(On(follow_path)) m_iFlags ^=follow_path;}
	void InterposeOff(){if(On(interpose)) m_iFlags ^=interpose;}
	void HideOff(){if(On(hide)) m_iFlags ^=hide;}
	void OffsetPursuitOff(){if(On(offset_pursuit)) m_iFlags ^=offset_pursuit;}
	void FlockingOff(){CohesionOff(); AlignmentOff(); SeparationOff(); WanderOff();}
	void ForwardResistanceOff(){if(On(forward_resistance)) m_iFlags ^= forward_resistance;}
	void ApproachOff(){if(On(approach)) m_iFlags ^= approach;}
	void ApproachHorizonOff(){if(On(approach_horizon)) m_iFlags ^= approach_horizon;}
	void CrowdPathOff(){if(On(crowd_path)) m_iFlags ^= crowd_path;}

	bool isFleeOn(){return On(flee);}
	bool isSeekOn(){return On(seek);}
	bool isArriveOn(){return On(arrive);}
	bool isWanderOn(){return On(wander);}
	bool isPursuitOn(){return On(pursuit);}
	bool isEvadeOn(){return On(evade);}
	bool isCohesionOn(){return On(cohesion);}
	bool isSeparationOn(){return On(separation);}
	bool isAlignmentOn(){return On(allignment);}
	bool isObstacleAvoidanceOn(){return On(obstacle_avoidance);}
	bool isWallAvoidanceOn(){return On(wall_avoidance);}
	bool isFollowPathOn(){return On(follow_path);}
	bool isInterposeOn(){return On(interpose);}
	bool isHideOn(){return On(hide);}
	bool isOffsetPursuitOn(){return On(offset_pursuit);}
	bool isApproachOn(){return On(approach);}
	bool isApproachHorizonOn(){return On(approach_horizon);}
	bool isCrowdPathOn(){return On(crowd_path);}

	const std::vector<Vector2D>& GetFeelers()const{return m_Feelers;}

	//double WanderJitter()const{return m_dWanderJitter;}
	//double WanderDistance()const{return m_dWanderDistance;}
	//double WanderRadius()const{return m_dWanderRadius;}

	//double SeparationWeight()const{return m_dWeightSeparation;}
	//double AlignmentWeight()const{return m_dWeightAlignment;}
	//double CohesionWeight()const{return m_dWeightCohesion;}

};


NS_AS_END

#endif